#include <tinx/debug.h>
#include <tinx/io.h>
#include <tinx/pci.h>
#include <tinx/list.h>
#include <tinx/arena.h>

#define PCI_BAR_ADDR 0x10 // BAR 在配置空间中的偏移量
#define PCI_BAR_NR 6

#define PCI_CONF_ADDR 0xCF8
#define PCI_CONF_DATA 0xCFC

#define PCI_ADDR(bus, dev, func, addr) \
        (u32)(0x80000000) | \
        (((u32)bus & 0xff) << 16) | \
        (((u32)dev & 0x1f) << 11) | \
        (((u32)func & 0x7) << 8) | \
        ((u32)addr & 0xff)

static list_t pci_device_list;

// PCI 设备的厂商信息
struct
{
    u32 classcode;
    char *name;
} pci_classnames[] = {
    {0x000000, "Non-VGA unclassified device"},
    {0x000100, "VGA compatible unclassified device"},
    {0x010000, "SCSI storage controller"},
    {0x010100, "IDE interface"},
    {0x010200, "Floppy disk controller"},
    {0x010300, "IPI bus controller"},
    {0x010400, "RAID bus controller"},
    {0x010601, "AHCI controller"},
    {0x018000, "Unknown mass storage controller"},
    {0x020000, "Ethernet controller"},
    {0x020100, "Token ring network controller"},
    {0x020200, "FDDI network controller"},
    {0x020300, "ATM network controller"},
    {0x020400, "ISDN controller"},
    {0x028000, "Network controller"},
    {0x030000, "VGA controller"},
    {0x030100, "XGA controller"},
    {0x030200, "3D controller"},
    {0x038000, "Display controller"},
    {0x040000, "Multimedia video controller"},
    {0x040100, "Multimedia audio controller"},
    {0x040200, "Computer telephony device"},
    {0x048000, "Multimedia controller"},
    {0x050000, "RAM memory"},
    {0x050100, "FLASH memory"},
    {0x058000, "Memory controller"},
    {0x060000, "Host bridge"},
    {0x060100, "ISA bridge"},
    {0x060200, "EISA bridge"},
    {0x060300, "MicroChannel bridge"},
    {0x060400, "PCI bridge"},
    {0x060500, "PCMCIA bridge"},
    {0x060600, "NuBus bridge"},
    {0x060700, "CardBus bridge"},
    {0x060800, "RACEway bridge"},
    {0x060900, "Semi-transparent PCI-to-PCI bridge"},
    {0x060A00, "InfiniBand to PCI host bridge"},
    {0x068000, "Bridge"},
    {0x070000, "Serial controller"},
    {0x070100, "Parallel controller"},
    {0x070200, "Multiport serial controller"},
    {0x070300, "Modem"},
    {0x078000, "Communication controller"},
    {0x080000, "PIC"},
    {0x080100, "DMA controller"},
    {0x080200, "Timer"},
    {0x080300, "RTC"},
    {0x080400, "PCI Hot-plug controller"},
    {0x088000, "System peripheral"},
    {0x090000, "Keyboard controller"},
    {0x090100, "Digitizer Pen"},
    {0x090200, "Mouse controller"},
    {0x090300, "Scanner controller"},
    {0x090400, "Gameport controller"},
    {0x098000, "Input device controller"},
    {0x0A0000, "Generic Docking Station"},
    {0x0A8000, "Docking Station"},
    {0x0B0000, "386"},
    {0x0B0100, "486"},
    {0x0B0200, "Pentium"},
    {0x0B1000, "Alpha"},
    {0x0B2000, "Power PC"},
    {0x0B3000, "MIPS"},
    {0x0B4000, "Co-processor"},
    {0x0C0000, "FireWire (IEEE 1394)"},
    {0x0C0100, "ACCESS Bus"},
    {0x0C0200, "SSA"},
    {0x0C0300, "USB Controller"},
    {0x0C0400, "Fiber Channel"},
    {0x0C0500, "SMBus"},
    {0x0C0600, "InfiniBand"},
    {0x0D0000, "IRDA controller"},
    {0x0D0100, "Consumer IR controller"},
    {0x0D1000, "RF controller"},
    {0x0D8000, "Wireless controller"},
    {0x0E0000, "I2O"},
    {0x0F0000, "Satellite TV controller"},
    {0x0F0100, "Satellite audio communication controller"},
    {0x0F0300, "Satellite voice communication controller"},
    {0x0F0400, "Satellite data communication controller"},
    {0x100000, "Network and computing encryption device"},
    {0x101000, "Entertainment encryption device"},
    {0x108000, "Encryption controller"},
    {0x110000, "DPIO module"},
    {0x110100, "Performance counters"},
    {0x111000, "Communication synchronizer"},
    {0x118000, "Signal processing controller"},
    {0x000000, NULL}};

u32 pci_outl(u8 bus, u8 dev, u8 func, u8 addr, u32 value)
{
    outl(PCI_CONF_ADDR, PCI_ADDR(bus, dev, func, addr));
    outl(PCI_CONF_DATA, value);
}

u32 pci_inl(u8 bus, u8 dev, u8 func, u8 addr)
{
    outl(PCI_CONF_ADDR, PCI_ADDR(bus, dev, func, addr));
    return inl(PCI_CONF_DATA);
}

static u32 pci_size(u32 base, int type)
{
    return ~(type & base) + 1;
}

pci_device_t *pci_find_device(u16 vendorid, u16 deviceid)
{
    for (list_node_t *ptr = pci_device_list.head.next; ptr != &pci_device_list.tail; ptr = ptr->next)
    {
        pci_device_t *device = element_entry(pci_device_t, node ,ptr);
        if (device->vendorid == vendorid && device->deviceid == deviceid)
            return device;
    }
    return NULL;
}

pci_device_t *pci_find_device_by_class(u32 classcode)
{
    for (list_node_t *ptr = pci_device_list.head.next; ptr != &pci_device_list.tail; ptr = ptr->next)
    {
        pci_device_t *device = element_entry(pci_device_t, node ,ptr);
        if (device->classcode == classcode)
            return device;
        if ((device->classcode & PCI_SUBCLASS_MASK) == classcode)
            return device;
    }
    return NULL;
}

int pci_find_bar(pci_device_t *device, pci_bar_t *bar, int nr)
{
    u32 addr = PCI_BAR_ADDR + nr * sizeof(u32); // 获得在配置空间中的偏移量
    u32 value = pci_inl(device->bus, device->dev, device->func, addr);
    pci_outl(device->bus, device->dev, device->func, addr, -1); // -1 在二进制中全是 1
    int len = pci_inl(device->bus, device->dev, device->func, addr);
    pci_outl(device->bus, device->dev, device->func, addr, value);

    if (value == 0)
        return EOF;

    if (len == 0 || len == -1)
        return EOF;

    if (value == -1)
        value = 0;

    if (value & 1)
    {
        bar->iobase = value & PCI_BAR_IO_MASK;
        bar->size = pci_size(len, PCI_BAR_IO_MASK);
    }
    if (!(value & 1))
    {
        bar->iobase = value & PCI_BAR_MEM_MASK;
        bar->size = pci_size(len, PCI_BAR_MEM_MASK);
    }
    return EOK;
}

// 获得 PCI 类型描述
char *pci_classname(u32 classcode)
{
    for (size_t i = 0; pci_classnames[i].name != NULL; i++)
    {
        if (pci_classnames[i].classcode == classcode)
            return pci_classnames[i].name;
        if (pci_classnames[i].classcode == (classcode & 0xFFFF00))
            return pci_classnames[i].name;
    }
    return "Unknown device";
}

void pci_check_device(u8 bus, u8 dev)
{
    u32 value = 0;

    for (size_t func = 0; func < 8; func++)
    {
        value = pci_inl(bus, dev, func, PCI_CONF_VENDOR);
        u16 vendorid = value & 0xffff;
        if (vendorid == 0 ||  vendorid == 0xffff)
            return;

        pci_device_t *device = (pci_device_t *)kmalloc(sizeof(pci_device_t));
        list_append(&pci_device_list, &device->node);
        device->bus = bus;
        device->dev = dev;
        device->func = func;
        device->vendorid = vendorid;
        
        value = pci_inl(bus, dev, func, PCI_CONF_DEVICE);
        device->deviceid = value & 0xffff;

        value = pci_inl(bus, dev, func, PCI_CONF_REVISION);
        device->classcode = value >> 8; // 获得设备的 classcode
        device->revision = value & 0xff;

        value = pci_inl(bus, dev, func, PCI_CONF_INTERRUPT);
        device->irq = value & 0xff;

        DEBUGK("PCI %02x:%02x.%x %5x %4x:%4x %s\n",
             device->bus, device->dev, device->func,
             device->classcode,
             device->vendorid, device->deviceid,
             pci_classname(device->classcode));
    }
}

void pci_send_command(pci_device_t *device, u8 cmd)
{
    // 获得原来的命令数据
    u32 data = pci_inl(device->bus, device->dev, device->func, PCI_CONF_COMMAND);
    data |= cmd; // 置位命令
    pci_outl(device->bus, device->dev, device->func, PCI_CONF_COMMAND, data);
}

// 枚举 PCI 设备
void pci_enum_device()
{
    for (size_t bus = 0; bus < 256; bus++)
    {
        for (size_t dev = 0; dev < 32; dev++)
        {
            pci_check_device(bus, dev);
        }
    }
}

void pci_init()
{
    list_init(&pci_device_list);
    pci_enum_device();
}